Skip to content

docs(migration): close v1-to-v2 guide gaps found while migrating sample dependents#2392

Merged
felixweinberger merged 7 commits into
mainfrom
fweinberger/w1-docs
Jun 30, 2026
Merged

docs(migration): close v1-to-v2 guide gaps found while migrating sample dependents#2392
felixweinberger merged 7 commits into
mainfrom
fweinberger/w1-docs

Conversation

@felixweinberger

Copy link
Copy Markdown
Contributor

Expands the v1→v2 migration guide with everything learned from migrating a bunch of sample dependents (CLI tools, web apps, gateway services, and monorepos of various shapes) using only the guide, the codemod, and the installed typings.

Motivation and Context

Running clean-room migrations of realistic dependents surfaces the questions the guide doesn't answer yet. Every place a migrator had to stop and figure something out became a guide addition. Highlights:

  • Staged migrations: ordering, v1/v2 coexistence boundaries (third-party packages, host-provided v1 peers), and the objects-don't-cross rule
  • Monorepos: workspace-member dependency handling, hoisted installs, and repo-local tooling that encodes the v1 package name (pin lints, dist-text reads)
  • Packaging and runtime: a Jest/CJS resolver recipe, bundler guidance for the nested zod copies, registry availability during the alpha
  • zod: compile-time vs runtime symptom split, per-member alias route for zod@3-pinned monorepos
  • Errors: per-transport status handling, error-name keyed matching, published-alpha qualifiers for behavior that differs between the latest alpha and main
  • Smaller items: completable nesting, InMemoryTransport linked pairs, notification-handler forms, typed tools/list returns, OAuth discovery state, timeout re-baselining

Also corrects a handful of existing claims that turned out to be inaccurate when checked against source (transport error-message comparison, registry availability, workspace-member wording).

How Has This Been Tested?

Each addition was verified against the SDK source at this branch's base commit before being written. docs:check and the snippet sync pass.

Breaking Changes

None — documentation only.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Companion to the codemod PR from the same migration trials; the two are independent and can land in either order.

…ion sweep

Adds staged-migration ordering, monorepo workspace-member dependency rules,
registry-availability notes for the alpha window, the zod compile-time vs
runtime symptom split, the completable optional-nesting caveat, gateway
inbound error reconstruction with its limits, repo-local tooling that
encodes the v1 package name, HeadersInit send-side clarification, the
published-alpha error-code qualifier, the InMemoryTransport linked-pair
rule, malformed inbound frame behavior, spec-form notification handler
examples, OAuth discovery-state and connect-time retry coverage, transport
compatibility and unchanged-API bullets, stdio write-failure and
session-header notes, third-party dependency guidance, timeout
re-baselining relief with per-era cancel-signal qualifiers, and the
manifest-handling section matching the codemod's nearest-manifest swap and
workspace-member report.
@felixweinberger felixweinberger requested a review from a team as a code owner June 30, 2026 10:02
@changeset-bot

changeset-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 231b796

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jun 30, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@2392

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@2392

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@2392

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@2392

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@2392

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@2392

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@2392

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@2392

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@2392

commit: 231b796

Comment thread docs/migration/upgrade-to-v2.md
Comment thread docs/migration/upgrade-to-v2.md Outdated
Comment thread docs/migration/upgrade-to-v2.md Outdated
felixweinberger and others added 5 commits June 30, 2026 12:46
…loseSSE gating note

Two corrections from review: v1-imported and v2-imported processes
negotiate through the ordinary 2025-era initialize handshake and settle on
the newest revision both packages support (the previous text hardcoded
2025-06-18, which neither side selects today), and ctx.http?.closeSSE is
populated only when the transport has an eventStore AND the client's
negotiated protocol version supports resumable close — an eventStore
transport serving an older client still leaves it undefined.
Adds the navigation layer the guide was missing: a symptom index mapping
literal compiler/runtime diagnostics to their sections, a by-situation
router, a full-depth table of contents, and a closing verification
checklist assembling the done-criteria greps. Splits the largest sections
by symptom (the zod floor gets its own findable heading), collapses the
duplicated codemod-coverage top matter into one routing layer, moves
content documented only in routing bullets into the sections they point
at, and gates trial-specific edge cases behind one-glance applicability
conditions.

Factual corrections: the two client-conformance checklists are unified
into one canonical superset; the unchanged-APIs lead no longer claims
import-path-only changes; published-alpha caveats are deduplicated and
stamped with a greppable marker; the hoisted-monorepo paragraph now
matches the codemod's actual behavior; and a section for library authors
that peer-depend on the SDK fills the one gap a cold-reader probe could
not answer. No behavioral guidance was removed; relocated text moves
verbatim.
Completes the OAuthClientProvider conformance checklist with the two
obligations the 2026-07-28 guide lists (discovery-state persistence and
the insufficient-scope choice), fixes the unchanged-APIs lead to match its
own entries, aligns the hoisted-monorepo guidance with the codemod's
actual root rewrite, adds a short section for library authors that
peer-depend on the SDK, and corrects two notes in the 2026-07-28 guide.
@felixweinberger felixweinberger merged commit 14160f7 into main Jun 30, 2026
18 checks passed
@felixweinberger felixweinberger deleted the fweinberger/w1-docs branch June 30, 2026 14:40

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional findings (outside current diff — PR may have been updated during review):

  • 🟡 docs/migration/support-2026-07-28.md:486-491 — The claim that "the exported *Schema constants validate the neutral model and reject it as an unknown key" overgeneralizes: only EmptyResultSchema is strict (ResultSchema.strict()), while ResultSchema itself and the other result schemas listed in this bullet (CallToolResultSchema, GetPromptResultSchema, …) are loose-passthrough ResultSchema.extend(...) builds, so parsing a frame carrying resultType with them succeeds and keeps the field. Suggest scoping the rejection claim to EmptyResultSchema (matching the guide's own wire-codecs table, which says guards "validate neutral shapes (loose passthrough)") or restoring the previous neutral wording.

    Extended reasoning...

    What the bug is. The rewritten bullet under "Wire-only members hidden from public types" (docs/migration/support-2026-07-28.md, lines 486–491) lists Result, CallToolResult, GetPromptResult as examples and then states that "the exported *Schema constants validate the neutral model and reject it as an unknown key." The plural subject ("the exported *Schema constants") in a bullet that just enumerated those result types reads as a claim about all of them — but the rejection behavior only exists for EmptyResultSchema.

    What the code actually does. In packages/core-internal/src/types/schemas.ts:

    • ResultSchema is z.looseObject({...}) (line 92), with a comment explicitly noting that resultType is deliberately NOT modeled in the neutral schemas — it lives only inside the 2026-era wire codec.
    • EmptyResultSchema = ResultSchema.strict() (line 180) is the only strict result schema; it alone rejects unknown keys such as resultType.
    • Every other exported result schema — GetPromptResultSchema (line 1229), ListToolsResultSchema (line 1371), CallToolResultSchema (line 1378), CreateMessageResultSchema, etc. — is built with ResultSchema.extend(...) (or PaginatedResultSchema.extend(...)), which retains the loose catchall in zod 4.

    Step-by-step proof. Take a raw 2026-era wire frame's result object: { resultType: 'complete', content: [{ type: 'text', text: 'hi' }] }. (1) CallToolResultSchema.parse(obj) succeeds — CallToolResultSchema is ResultSchema.extend({ content: ..., ... }), and the loose base passes resultType through unchanged, so the parsed value still carries resultType: 'complete'. (2) The same is true for GetPromptResultSchema, ListToolsResultSchema, and the rest. (3) Only EmptyResultSchema.parse({ resultType: 'complete' }) throws, because .strict() rejects unknown keys. So the documented behavior — rejection as an unknown key — holds for exactly one of the schemas the bullet's subject covers.

    Why it matters. This bullet is targeted at v2-alpha migrators who previously read resultType directly. A migrator who validates raw wire frames with e.g. CallToolResultSchema and trusts this sentence will expect a parse failure on a frame carrying resultType (and may rely on that for traffic classification or sanitization); the parse passes and the field flows through. The sentence also conflicts with the same guide's own wire-codecs table a few lines up (line ~463), whose last row says guards/schemas "validate neutral shapes (loose passthrough); validate raw wire traffic with a transport-level parse." The pre-PR wording ("The wire schemas keep parsing it, and the protocol layer consumes it before results reach your code") made no rejection claim, so this inaccuracy is introduced by the diff. The trailing parenthetical pointing to "the EmptyResultSchema row" softens but does not scope the plural-subject sentence.

    Why nothing else catches it. docs:check and snippet sync validate links and code blocks, not prose claims about schema strictness, and no verifier objected — all four confirmations independently checked schemas.ts and agreed.

    How to fix. Scope the claim to the one strict schema, e.g.: "…the exported *Schema constants validate the neutral model and pass unknown keys through — only EmptyResultSchema rejects resultType as an unknown key (the EmptyResultSchema row in the Per-era wire codecs table)." Or simply restore the previous neutral wording. One-clause change, docs only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant